#include "messageComputer.h"

extern trackerData* globalTrackerData;

// traite le message en fonction de son type
char* computeMessage(message* m, char* ip)
{
  char* response;
  switch(m->type)
    {
    case ANNOUNCE:
      {
	char* port = announce_getListenPort(m->data);
	char* seedList = announce_getSeedList(m->data);
	char* leechList = announce_getLeechList(m->data);
	response = computeAnnounce(port, seedList, leechList, ip);	
	dumpTrackerData();
      } break;
    case LOOK:
      {
	DBG("%s","Parsing param for LOOK\n");
	char* criterionList = look_getCriterionList(m->data);
	response = computeLook(criterionList);
      } break;
    case GETFILE:
      {
	char* key = getfile_getKey(m->data);
	response = computeGetFile(key);
      } break;
    case UPDATE:
      {
	DBG("%s","Parsing param for UPDATE\n");
	char* seedList = update_getSeedList(m->data);
	char* leechList = update_getLeechList(m->data);
	response = computeUpdate(seedList, leechList, ip);
      } break;
    case UNKNOWN:
      {
	assert(0 && "This should not happen");
	response = "ERROR";
      } break;
    default:
      {
	assert(0 && "This should not happen");
	response = "ERROR";
      }
    }
  return response;
}

/*
 * Cette fonction doit :
 * - ajouter le peer à la liste des peer si il n'existe pas
 * - ajouter les fichiers et leurs informations à la liste des fichiers
 * - ajouter le peer à la liste des seeder de chaque ficheir la liste de leech
 * - renvoyer "ok"
 */
char* computeAnnounce(char* port, char* seedList, char* leechList, char* ip)
{
  DBG("peer ip = %s\n", ip);
  peer* p = getPeerByIp(ip);
  if (p == NULL)
    {
      // si le peer n'est pas connu, on l'ajoute
      p = createPeer(ip, port);
      addPeer(p);
    }
  // on parse la seedList 
  DBG("%s","parsing seed list\n");
  if (seedList != NULL)
    {
      hashtable* peerFileList = announce_parseSeedList(seedList);
      if (peerFileList == NULL)
	{
	  return strdup("error");
	}
      int i;
      for (i=0;i<peerFileList->nbItem;i++)
	{
	  // on vérifie si le fichier est connu
	  if (!(existKey(peerFileList->keyArray[i], globalTrackerData->fileList)))
	    {
	      // Le fichier est nouveau, on l'ajoute à la base
	      addFileToTrackerData(peerFileList->keyArray[i], (file*)peerFileList->dataArray[i]);
	    }
	  // on ajoute ensuite le seeder
	  addSeederForFile(peerFileList->keyArray[i], p);	  
	}
    }
  DBG("%s","parsing leech list\n");
  // on parse la leechList
  if (leechList != NULL)
    {
      list* leechedKeys = announce_parseLeechList(leechList);
      // pour chaque clef
      while (leechedKeys != NULL)
	{
	  addSeederForFile((char*)(leechedKeys->data), p);
	  leechedKeys = removeElementList(leechedKeys);
	}
    }
  char* strReturn = malloc(sizeof(char) * 3);
  strcpy(strReturn,"ok");
  return strReturn;
}


char* computeLook(char* criterionList)
{
  list* criterions = parseCriterionList(criterionList);
  list* validFile = NULL;
  list* tempFileList = NULL; 
  int firstRun = 1;
  DBG("%s","Validating criterions\n");
  while(criterions != NULL)
    { 
      criterion* c = parseCriterion((char*)criterions->data);
      // c est NULL si le critère de recherche est mal formé
      // dans ce cas on renvoit une erreur au client
      if (c == NULL)
	{
	  return strdup("error - unknown criterion");
	}
      DBG("%s","Checking for criterion : \n");
      dumpCriterion(c);
      //      dumpList(tempFileList);
      while(tempFileList != NULL || firstRun == 1)
	{
	  if (firstRun)
	    {
	      DBG("%s","firstRun for checking criterion\n");
	      int i;
	      for (i=0;i<globalTrackerData->fileList->nbItem;i++)
		{
		  if (validateCriterion(c,(file*)(globalTrackerData->fileList->dataArray[i])))
		    {	    
		      validFile = 
			addElementToList(globalTrackerData->fileList->dataArray[i], validFile);
		    }
		}
	      firstRun = 0;
	    }
	  else
	    {
	      DBG("tempFileList = %p\n", tempFileList);
	      DBG("check File %s\n", ((file*)(tempFileList->data))->fileName);
	      if (validateCriterion(c,(file*)(tempFileList->data)))
		{
		  DBG("File %s validate Criterion !\n",
			 ((file*)(tempFileList->data))->fileName);
		  validFile = addElementToList(tempFileList->data,validFile);
		  DBG("File %s has been added to the valid File list\n",
			 ((file*)(tempFileList->data))->fileName);
		}
	      list* oldTemp = tempFileList;
	      tempFileList = tempFileList->next;
	      removeElementList(oldTemp);
	    }
	}
      criterions = criterions->next;
      /*
      DBG("next criterion is : \n");
      dumpCriterion(parseCriterion((char*) criterions->data));
      */
      tempFileList = validFile;
    }
  #ifdef DEBUG
  DBG("%s","end of parsing\n");
  DBG("%s","Dumping found files\n");
  dumpList(validFile);
  #endif
  // si il n'y a pas de fichiers correspondant, on renvoit "list []" 
  if (validFile == NULL)
    {
      DBG("%s","LOOK failed to find any file\n");
      return strdup("list []");
    }
  else
    {
      char* strFileList = fileListToString(validFile);
      char* strReturn = malloc(sizeof(char) * 5120);
      sprintf(strReturn,"list [%s]", strFileList);
      free(strFileList);
      return strReturn;
    }
}

char* computeGetFile(char* key)
{
  file* f = (file*) (getData(key, globalTrackerData->fileList)); // récupération du fichier correspondant à key
  if (f != NULL)
    {
      char* strReturn = malloc(sizeof(char) *5120 );
      list* peerList = f->seeders; // on récupère la liste des seeders pour ce fichier
      if (peerList == NULL) // si on ne trouve pas de seeder, on renvoit NULL
	{
	  WG("No peer found for file %s\n", f->fileName);
	  return NULL;
	}
      // on transforme la liste de seeder en une chaine de caractères
      char* strPeerList = peerListToString(peerList);
      sprintf(strReturn, "peers %s [%s]", key, strPeerList);
      free(strPeerList);
      return strReturn;
    }
  else
    {
      WG("No file found with key %s\n", key);
      return strdup("error - no peer for this file");;
    }
}

char* computeUpdate(char* seedList, char* leechList, char* ip)
{
  DBG("%s","Computing update\n");
  // on récupère le pair concerné
  peer* currentPeer = getPeerByIp(ip);
  char* strReturn = malloc(sizeof(char) * 8);
  if (currentPeer == NULL)
    {
      // si on ne trouve pas le pair, on renvoit une erreur
      WG("%s","unknown peer, no previous announce from this peer\n");
      strcpy(strReturn,"error");
      return strReturn;
    }
  else
    {
      // on a trouvé le pair, il faut désormais mettre à jour les informations de fichiers
      DBG("%s","Peer found, updating seed/leech list\n");
      list* keyList = update_parseLeechSeedList(seedList, leechList);
      DBG("%s","dumping keyList\n");
      dumpList(keyList);
      while(keyList != NULL)
	{
	  DBG("Adding a seeder to file %s\n", (char*)(keyList->data));
	  addSeederForFile((char*)(keyList->data), currentPeer);
	  keyList = removeElementList(keyList);
	}
      strcpy(strReturn,"ok");
      return strReturn;
    }
}

peer* getPeerByIp(char* ip)
{
  list* peerList = globalTrackerData->peerList;
  while(peerList != NULL)
    {
      peer* tmpPeer = (peer*)peerList->data;
      if (strstr(tmpPeer->ip,ip) != NULL)
	return tmpPeer;
      peerList = peerList->next;
    }
  return NULL;
}

void addPeer(peer* p)
{
  assert(p != NULL);
  globalTrackerData->peerList = addElementToList((void*)p, globalTrackerData->peerList);
}
